// 默认情况下，Rust会将少量标准库中定义的程序项（item）引入到每个程序的作用域中。
// 这些项称作 prelude，例如： println! 宏就是来自标准库
// 如果需要的类型不在 prelude 中，你必须使用 use 语句显式地将其引入作用域
use std::io;
use rand::Rng;
use std::cmp::Ordering;
// 对于所有的 expect() 和 unwrap() 异常抛出，我们都可以使用 match 来处理其中的 Result 返回


// 模块中的函数默认是私有的，只能在当前文件中使用。如果想要被外部引用，需要添加 pub 修饰以表明是公开类型
pub fn guess_demo() {
    println!("Guess the number!");
    println!("Please input your guess.");

    let apples = 5; // 不可变
    let mut bananas = 5; // 可变
    println!("the bananas are: {}", bananas);
    bananas = 10;
    // 里面的 {} 是预留在特定位置的占位符
    println!("the apples are: {}, and the bananas are: {}", apples, bananas);

    // :: 双引号用于引用 crate 中的模块，而 . 点引用模块中的成员
    // 调用 rand::thread_rng 函数来为我们提供将要使用的特定随机数生成器：它位于当前执行线程的本地环境中，
    // 并从操作系统获取 seed。然后我们调用随机数生成器的 gen_range 方法。区间左闭右开
    // let secret_number = rand::thread_rng().gen_range(1..101);
    // 等价上面取值范围表示
    let secret_number = rand::thread_rng().gen_range(1..=100);
    println!("the secret_numer is: {}", secret_number);


    // loop 关键字创建了一个无限循环
    loop {
        println!("please input your guess number!");

        // 在 Rust 中，变量默认是不可变的，想要让变量可变，可以在变量名前添加 mut（mutability，可变性）
        // 默认的变量是一次性的，不论该变量是否被 mut 修饰，
        let mut guess = String::new();

        // io 库来自于标准库，标准库也被称为 std
        match io::stdin()
            // 将 &mut guess 作为参数传递给 read_line()，以告诉它在哪个字符串存储用户输入
            // & 表示这个参数是一个引用（reference）
            // 和变量一样，引用默认是不可变的。因此，需要写成 &mut guess 来使其可变，而不是 &guess。因为用户输入的内容是可变的
            .read_line(&mut guess) {
            Ok(n) => {
                println!("{n} bytes read");
                println!("{guess}");
            }
            Err(error) => println!("error: {error}"),
        }

        // 处理用户输入，必须是数字
        // 字符串的 parse 方法 将字符串解析成数字。因为这个方法可以解析多种数字类型，
        // 因此需要告诉 Rust 具体的数字类型，这里通过 let guess: u32 指定。guess 后面的冒号（:）告诉 Rust 我们指定了变量的类型。
        // let guess: u32 = guess.trim().parse().expect("please input number!");
        // 处理无效输入，不要在用户输入非数字时崩溃，需要忽略非数字，让用户可以继续猜测
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue
        };

        println!("You guessed: {}", guess);

        // cmp 方法用来比较两个值并可以在任何可比较的值上调用。它获取一个被比较值的引用
        match guess.cmp(&secret_number) {
            Ordering::Less => {println!("too small")}
            Ordering::Equal => {
                println!("you win");
                break
            }
            Ordering::Greater => {println!("too big")}
        }
    }

}
